﻿namespace Vibstudio.X4NET.IO.INI.Model.Configuration
{
    #region Namespaces

    using System;
    using System.Reflection;
    using System.Text.RegularExpressions;

    #endregion

    public class BaseIniParserConfiguration : IIniParserConfiguration
    {
        #region Constants

        protected const string STR_COMMENT_REGEX = @"^{0}(.*)";

        protected const string STR_KEY_REGEX = @"^(\s*[_\.\d\w]*\s*)";

        protected const string STR_SECTION_REGEX_END = @"(\s*?)$";

        protected const string STR_SECTION_REGEX_MIDDLE = @"{1}\s*[_\{\}\#\+\;\%\(\)\=\?\&\$\,\:\/\.\-\w\d\s\\\~]+\s*";

        protected const string STR_SECTION_REGEX_START = @"^(\s*?)";

        protected const string STR_SPECIAL_REGEX_CHARS = @"[\^$.|?*+()";

        protected const string STR_VALUE_REGEX = @"([\s\d\w\W\.]*)$";

        #endregion

        #region Fields

        private string _commentString;

        private char _sectionEndChar;

        private char _sectionStartChar;

        #endregion

        #region Ctor(s)

        public BaseIniParserConfiguration(IIniParserConfiguration ori)
        {
            AllowDuplicateKeys = ori.AllowDuplicateKeys;
            OverrideDuplicateKeys = ori.OverrideDuplicateKeys;
            AllowDuplicateSections = ori.AllowDuplicateSections;
            AllowKeysWithoutSection = ori.AllowKeysWithoutSection;

            SectionStartChar = ori.SectionStartChar;
            SectionEndChar = ori.SectionEndChar;
            CommentString = ori.CommentString;
            ThrowExceptionsOnError = ori.ThrowExceptionsOnError;
        }

        protected BaseIniParserConfiguration()
        {
        }

        #endregion

        #region Public Properties

        public bool AllowDuplicateKeys { get; set; }

        public bool AllowDuplicateSections { get; set; }

        public bool AllowKeysWithoutSection { get; set; }

        public string AssigmentSpacer { get; set; }

        public Regex CommentRegex { get; set; }

        public string CommentString
        {
            get { return _commentString ?? string.Empty; }
            set
            {
                CommentRegex = new Regex(string.Format(STR_COMMENT_REGEX, value));
                _commentString = value;
            }
        }

        public char KeyValueAssigmentChar { get; set; }

        public bool OverrideDuplicateKeys { get; set; }

        public char SectionEndChar
        {
            get { return _sectionEndChar; }
            set
            {
                _sectionEndChar = value;
                RecreateSectionRegex(_sectionEndChar);
            }
        }

        public Regex SectionRegex { get; set; }

        public char SectionStartChar
        {
            get { return _sectionStartChar; }
            set
            {
                _sectionStartChar = value;
                RecreateSectionRegex(_sectionStartChar);
            }
        }

        public bool SkipInvalidLines { get; set; }

        public bool ThrowExceptionsOnError { get; set; }

        #endregion

        #region Public Methods and Operators

        public IIniParserConfiguration Clone()
        {
            return MemberwiseClone() as IIniParserConfiguration;
        }

        public override bool Equals(object obj)
        {
            var copyObj = obj as BaseIniParserConfiguration;
            if (copyObj == null)
            {
                return false;
            }

            Type oriType = GetType();
            try
            {
                foreach (PropertyInfo property in oriType.GetProperties())
                {
                    if (property.GetValue(copyObj, null).Equals(property.GetValue(this, null)))
                    {
                        return false;
                    }
                }
            }
            catch
            {
                return false;
            }

            return true;
        }

        #endregion

        #region Explicit Interface Methods

        object ICloneable.Clone()
        {
            return Clone();
        }

        #endregion

        #region Methods

        private void RecreateSectionRegex(char value)
        {
            if (char.IsControl(value) ||
                char.IsWhiteSpace(value) ||
                CommentString.Contains(new string(new[] { value })) ||
                value == KeyValueAssigmentChar)
            {
                throw new Exception(string.Format("Invalid character for section delimiter: '{0}", value));
            }

            string builtRegexString = STR_SECTION_REGEX_START;

            if (STR_SPECIAL_REGEX_CHARS.Contains(new string(_sectionStartChar, 1)))
            {
                builtRegexString += "\\" + _sectionStartChar;
            }
            else
            {
                builtRegexString += _sectionStartChar;
            }

            builtRegexString += STR_SECTION_REGEX_MIDDLE;

            if (STR_SPECIAL_REGEX_CHARS.Contains(new string(_sectionEndChar, 1)))
            {
                builtRegexString += "\\" + _sectionEndChar;
            }
            else
            {
                builtRegexString += _sectionEndChar;
            }

            builtRegexString += STR_SECTION_REGEX_END;

            SectionRegex = new Regex(builtRegexString);
        }

        #endregion
    }
}